using System;
using System.Collections.Generic;
using System.Text;
using PandoraCommon;
using System.Windows.Forms;
using System.Globalization;
using System.IO;
using System.Threading;

namespace ChronoGPS
{
    public class LogReader : ILogReader
    {
        int dateFieldIndex = 3;
        int latitudeFieldIndex = 0;
        int longitudeFieldIndex = 1;
        int altitudeFieldIndex = 2;
        int speedFieldIndex = 6;


        #region ILogReader Members

        /// <summary>
        /// Name of the log reader plug-in.
        /// </summary>
        /// <value></value>
        public string Name
        {
            get { return "ChronoGPS log file reader"; }
        }

        /// <summary>
        /// Description for the log reader plug-in.
        /// </summary>
        /// <value></value>
        public string Description
        {
            get { return "Log reader for the ChronoGPS log files (http://www.chronogps.com)"; }
        }

        /// <summary>
        /// Author of this log reader plug-in.
        /// </summary>
        /// <value></value>
        public string Author
        {
            get { return "2008 (c) Mario Sobral"; }
        }

        /// <summary>
        /// Reads the necessary GPS position information from its associated positions source (file, database, GPS unit, etc...)
        /// and generates a list of <see cref="T:GpsPosition"> to be processed by the Pandora software.</see>
        /// </summary>
        /// <returns>A list of <see cref="T:GpsPosition"/></returns>
        public List<GpsPosition> ReadLog()
        {
            List<GpsPosition> positions = new List<GpsPosition>();

            OpenFileDialog openLogFileDialog = new OpenFileDialog();
            openLogFileDialog.CheckFileExists = true;
            openLogFileDialog.CheckPathExists = true;
            openLogFileDialog.DefaultExt = "txt";
            openLogFileDialog.FileName = "*.txt";
            openLogFileDialog.Filter = "ChronoGPS log files (*.txt)|*.txt";
            openLogFileDialog.InitialDirectory = Application.ExecutablePath;
            openLogFileDialog.Multiselect = false;
            openLogFileDialog.Title = "Select the ChronoGPS log file to read";
            if (openLogFileDialog.ShowDialog() == DialogResult.OK)
            {
                CultureInfo currentCulture = Thread.CurrentThread.CurrentCulture;
                TextReader logFileReader = new StreamReader(openLogFileDialog.FileName);
                GpsPosition currentPosition;
                GpsPosition previousPosition = null;
                string newLine = string.Empty;

                try
                {
                    newLine = logFileReader.ReadLine(); // read first line 

                    while (newLine != null && newLine.Trim() != string.Empty)
                    {
                        
                        string[] rowValues = newLine.Split(';');

                        if (rowValues[0] != "?")
                        {
                            currentPosition = new GpsPosition();
                            Thread.CurrentThread.CurrentCulture = new CultureInfo("en-US");


                            DateTime newDate = GetTimeValue(rowValues[dateFieldIndex]);

                            currentPosition.Date = newDate;

                            currentPosition.Latitude = double.Parse(rowValues[latitudeFieldIndex]);
                            currentPosition.Longitude = double.Parse(rowValues[longitudeFieldIndex]);
                            currentPosition.Speed = double.Parse(rowValues[speedFieldIndex]) / 1000 * 3600; // m/s to Km/h
                            if (rowValues[6] != string.Empty)
                                currentPosition.Altitude = double.Parse(rowValues[altitudeFieldIndex]);

                            if (previousPosition != null)
                                CalculateAdditionalPositionData(currentPosition, previousPosition);

                            Thread.CurrentThread.CurrentCulture = currentCulture;

                            positions.Add(currentPosition);

                            previousPosition = currentPosition;
                        }
                        
                        newLine = logFileReader.ReadLine(); // Read next line
                    }

                }
                catch (Exception ex)
                {
                    string errorMessage = "Error reading ChronoGPS data file!" + Environment.NewLine + Environment.NewLine;
                    if (newLine != string.Empty)
                        errorMessage += "Problem in data line: " + newLine + Environment.NewLine + Environment.NewLine;
                    errorMessage += ex.Message + Environment.NewLine + Environment.NewLine + "Stack trace: " + ex.StackTrace;

                    MessageBox.Show(errorMessage);
                }
                finally
                {
                    Thread.CurrentThread.CurrentCulture = currentCulture;
                    if (logFileReader != null)
                        logFileReader.Close();
                }
            }

            MessageBox.Show(positions.Count.ToString() + " position(s) read from " + openLogFileDialog.FileName, this.Name);

            return positions;
        }

        #endregion

        


        /// <summary>
        /// Processes the current position's data that requires a previous position.
        /// </summary>
        /// <param name="previousPosition">The previous position.</param>
        private void CalculateAdditionalPositionData(GpsPosition currentPosition, GpsPosition previousPosition)
        {
            if (Math.Round(previousPosition.Speed) < Math.Round(currentPosition.Speed))
                currentPosition.IsAccelerating = true;
            else if (Math.Round(previousPosition.Speed) > Math.Round(currentPosition.Speed))
                currentPosition.IsBreaking = true;

            currentPosition.DistanceToPreviousPosition = Math.Round(currentPosition.DistanceTo(previousPosition) * 1000);
            currentPosition.TimeSincePreviousPosition = currentPosition.TimeSince(previousPosition);

            TimeSpan timeDiff = currentPosition.Date.Subtract(previousPosition.Date);

            if (timeDiff.TotalMilliseconds > 0)
            {
                double speedIncrease = (currentPosition.Speed - previousPosition.Speed) * 1000 / 3600; // in meters / second
                currentPosition.Acceleration = Gforce.CalculateLongitudinalAcceleration(previousPosition, currentPosition);   // Math.Round(speedIncrease / (timeDiff.TotalMilliseconds / 1000), 2);
            }
            else
                currentPosition.Acceleration = previousPosition.Acceleration;

            if (previousPosition != null)
                currentPosition.LateralAcceleration = Gforce.CalculateLateralAcceleration(previousPosition, currentPosition);
            else
                currentPosition.LateralAcceleration = 0;
        }


        /// <summary>
        /// Gets the index of the field.
        /// </summary>
        /// <param name="fieldName">Name of the field.</param>
        /// <param name="fields">The field names array.</param>
        /// <returns></returns>
        private static int getFieldIndex(string fieldName, string[] fields)
        {
            for (int i = 0; i < fields.Length; i++)
            {
                if (fields[i].ToUpper() == fieldName.ToUpper())
                    return i;
            }

            return -1;
        }

        private DateTime GetTimeValue(string newTimeFieldValue)
        {
            string[] newTimeValues = newTimeFieldValue.Split(':');
            int miliseconds = int.Parse(newTimeValues[3]);
            int seconds = int.Parse(newTimeValues[2]);
            int minutes = int.Parse(newTimeValues[1]);
            int hour = int.Parse(newTimeValues[0]);

            DateTime newTime = new DateTime(DateTime.Now.Year, DateTime.Now.Month, DateTime.Now.Day, hour, minutes, seconds);
            newTime.AddMilliseconds(miliseconds * 10);

            return newTime;
        }
    }
}
